package cn.mdmbct.douyin.common.proxy.invoker;

import cn.mdmbct.douyin.common.executor.ServiceExecutor;
import cn.mdmbct.douyin.common.proxy.ServiceExecutorProvider;
import cn.mdmbct.douyin.common.proxy.annotations.Mapping;
import cn.mdmbct.douyin.common.proxy.annotations.Provider;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 调用标有{@link Provider}注解的方法
 *
 * @author mdmbct  mdmbct@outlook.com
 * @date 5/10/2021 10:54 PM
 * @modified mdmbct
 * @since 1.0
 */
public class ServiceMethodWithProviderInvoker<T> implements ServiceMethodInvoker<T> {

    private final ServiceExecutor serviceExecutor;

    public ServiceMethodWithProviderInvoker(ServiceExecutor serviceExecutor) {
        this.serviceExecutor = serviceExecutor;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {


        Provider provider = method.getDeclaredAnnotation(Provider.class);

        Class<ServiceExecutorProvider<T>> providerClass = (Class<ServiceExecutorProvider<T>>) provider.value();
        // 拿到构造函数
        Constructor<ServiceExecutorProvider<T>> declaredConstructor = providerClass.getDeclaredConstructor(ServiceExecutor.class, proxy.getClass());
        // 反射调用构造函数创建实例
        ServiceExecutorProvider<T> serviceExecutorProvider = declaredConstructor.newInstance(serviceExecutor, proxy);

        List<Class<?>> argClasses = Arrays.stream(args).map(Object::getClass).collect(Collectors.toList());

        // 添加Api
        if (provider.addApiParam()) {
            argClasses.add(Mapping.class);
            Method declaredMethod = providerClass.getDeclaredMethod(method.getName(), argClasses.toArray(new Class[0]));
            return declaredMethod.invoke(serviceExecutorProvider, method.getDeclaredAnnotation(Mapping.class), args);
        }

        Method declaredMethod = providerClass.getDeclaredMethod(method.getName(), argClasses.toArray(new Class[0]));
        return declaredMethod.invoke(serviceExecutorProvider, args);

    }
}
